Константные указатели, указатели-константы и указатели на константы
Очень много ошибок в программировании связано с неправильным или непредсказуемым изменением переменных. Даже в простом приложении можно напутать порядок работы или забыть удалить ресурсы. В многопоточных приложениях обобщённые ресурсы становятся настоящей головной болью. При совместной разработке, если функции позволяют изменять значение переданного аргумента, рано или поздно кто-нибудь обязательно их изменит неправильным образом.
Один из методов борьбы с такими ошибками – это запрет модификации объекта. В си можно создавать такие указатели, которые могут изменять своё значение (то есть, их можно переприсваивать), но при этом они могут только читать содержимое памяти и не могут его изменять.
#include <conio.h>
#include <stdio.h>
void main() {
const char* p = NULL;
unsigned length = 0;
char word[] = "Hello world!";
p = word;
while (*p++) {
length++;
}
//Операция *p = '?' запрещена и приведёт к ошибке
printf("length(\"%s\") = %d", word, length);
_getch();
}
Использование константных указателей позволяет значительно обезопасить работу. Например, если мы используем функцию, и она принимает константный указатель, то мы уверены, что она не изменит наши данные.
#include <conio.h>
#include <stdio.h>
unsigned getSafeLength(const char *word) {
const char *p = word;
unsigned length = 0;
if (word != NULL) {
while (*p++) {
length++;
}
}
return length;
}
unsigned getUnsafeLength(char *word) {
char *p = word;
unsigned length = 0;
if (word != NULL) {
while (*p++) {
//HAHAHA! EVIL ACTIVITY!
if (*p == 'A') {
*p = 'B';
}
length++;
}
}
return length;
}
void main() {
const char* p = NULL;
unsigned length = 0;
char word[] = "Hay guys! Right answer for #3 question is A";
printf("length(\"%s\") = %d\n", word, getSafeLength(word));
printf("length(\"%s\") = %d\n", word, getUnsafeLength(word));
_getch();
}
Когда мы видим код программы, у нас есть возможность узнать, меняются ли переданные аргументы или нет. Но это уже требует времени. Если мы пользуемся чужими библиотеками, то у нас есть доступ только до документации или до h-файлов.
unsigned getSafeLength(const char *);
unsigned getUnsafeLength(char *);
Из определения функции видно, что первая функция не может изменить аргумента, о второй ничего не известно.
Старайтесь использовать константные указатели, где есть возможность.
Указатель может быть константой сам по себе. Это значит, что нельзя изменить содержимого указателя, но с помощью него можно получить доступ до содержимого объекта, на который он ссылается.
#include <conio.h>
#include <stdio.h>
void main() {
int x = 100;
int *const p = &x;
printf("x = %d\n", x);
*p = 200;
printf("x = %d", x);
//операция p = &? или p++ и т.п. запрещены
_getch();
}
Можно комбинировать, и создать константный указатель константу. Возможно, это когда-нибудь пригодится.
#include <conio.h>
#include <stdio.h>
void main() {
int x = 100;
const int *const p = &x;
printf("x = %d\n", x);
printf("x = %d", *p);
_getch();
}
Можно также хранить указатель константы. Так как это константа, то необходимо, чтобы указатель тоже был константным.
#include <conio.h>
#include <stdio.h>
void main() {
const int x = 100;
const int *p;
p = &x;
printf("x = %d\n", x);
printf("x = %d", *p);
_getch();
}
Ну и самый жуткий пример: константный указатель константа на константу. Область применения: сферическое программирование в вакууме.
Заметьте, const char *p
и char const *p
ничем не отличаются. Каких-либо условий, когда как писать далее делать не будем.